#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include <iostream>
#include <memory>
#include <errno.h>
#include <strings.h>
#include <string.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>


using namespace LogModule;
const static int gsockfd = -1;
const static uint16_t gport = 8080;
// const static std::string gip = "127.0.0.1";

class UdpServer
{
public:
    UdpServer(uint16_t port = gport)//服务器不需要bind ip 地址，一个服务器可能有多个ip地址，如果bind ip地址的话，就只可以接收访问这个ip地址的信息
    :_sockfd(gsockfd),
    _addr(port),
    _is_running(false)
    {}
    void InitServer()
    {
        // 创建socket
        _sockfd = ::socket(AF_INET,SOCK_DGRAM,0);
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket : " << strerror(errno);
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;
        // 填充网络信息，bind绑定
        // struct sockaddr_in local;
        // bzero(&local,sizeof(local));
        // local.sin_addr.s_addr = inet_addr(_ip.c_str());//string -> 4type , 4type-> network
        //,用于将点分十进制的 IP 地址字符串转换为网络字节序的 32 位无符号整数。
        // local.sin_addr.s_addr = INADDR_ANY;//可以接收任意服务器ip地址，用于绑定套接字（socket）时，允许套接字监听所有可用的网络接口。

        // local.sin_family = AF_INET;
        // local.sin_port = htons(_port);//主机转网络，将端口号字节序转换为大端字节序的形式在网络中进行传输
        //，用于确保不同字节序的计算机系统之间能够正确地交换数据。
        // bind：sockaddr_in设置进入内核

        int n = ::bind(_sockfd,_addr.NetAddr(),_addr.NetAddrLen());//将一个地址分配给套接字，这个操作被称为“给套接字分配一个名字”

        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind : " << strerror(errno);
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";

    }
    void Start()
    {
        _is_running = true;
        while(true)
        {
            struct sockaddr_in peer;
            bzero(&peer,sizeof(peer));
            socklen_t len = sizeof(peer);
            char buffer[1024];
            bzero(buffer,sizeof(buffer));

            ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer) - 1,0,CONV(&peer),&len);
            if(n > 0)
            {
                buffer[n] = 0;
                InetAddr cli(peer);
                // uint16_t port = ::ntohs(peer.sin_port);//网络字节序转主机字节序，端口号为16字节，就用ntohs,用于uint_16（2字节）之间的转换
                // std::string clientip = ::inet_ntoa(peer.sin_addr);
                
                std::string clientinfo = cli.Ip() + ":" + std::to_string(cli.Port()) + " # " + buffer;
                LOG(LogLevel::DEBUG) << clientinfo;
                std::string echo_string = "echo# ";
                echo_string += buffer;
                ::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));
            }
            _is_running = false;

        }   
    }
    ~UdpServer()
    {
        if(_sockfd > gsockfd)
            ::close(_sockfd);       
    }
private:
    int _sockfd;
    // int _port;
    // std::string _ip;
    bool _is_running;
    InetAddr _addr;
};

#endif